/**
 * 跨操作系统统一接口
 */

#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <io.h>
#include <time.h>
#include <sys/stat.h>

#include <stdio.h>
#include "unios.h"
#include "utils.h"

#include "log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "unios"
#include "log4app.h"


// ======================================================================================
// 基础功能
// ======================================================================================

void* unios_malloc(size_t size) { return malloc(size); }
void unios_free(void *ptr) { if(ptr) free(ptr); }
void unios_disable_interrupts() {}
void unios_enable_interrupts() {}
bool unios_is_in_isr() {return false; }
void unios_watchdog_feed() {}


// ======================================================================================
// 关于时钟
// ======================================================================================

unios_us_t unios_us_now() {
    LARGE_INTEGER sysfreq, ts;
    QueryPerformanceFrequency(&sysfreq);
    QueryPerformanceCounter(&ts);
    return ts.QuadPart * 1000000 / sysfreq.QuadPart;
}

void unios_us_delay(unios_us_t time_us) {
	LARGE_INTEGER sysfreq, ts, te;
	size_t elapsed;
    // 使用高精度计时器进行微秒延时
    QueryPerformanceFrequency(&sysfreq);
	QueryPerformanceCounter(&ts);

	do {
		QueryPerformanceCounter(&te);
		elapsed = (te.QuadPart - ts.QuadPart) * 1000000 / sysfreq.QuadPart;
	} while (elapsed < time_us);
}

void unios_ms_delay(unios_ms_t ms) {
    Sleep((DWORD)ms);
}

unios_ms_t unios_ms_now() {
    // 自系统启动以来经过的毫秒数
    return GetTickCount64();
}

unios_sec_t unios_rtcsec_get() {
	return (unios_sec_t)time(NULL);
}

std_err_t unios_rtcsec_set(unios_sec_t sec) {
    SYSTEMTIME st = { 0 };
    struct tm *tm2 = localtime((time_t*)&sec);
    st.wYear = tm2->tm_year; st.wMonth  = tm2->tm_mon; st.wDay    = tm2->tm_mday;
    st.wHour = tm2->tm_hour; st.wMinute = tm2->tm_min; st.wSecond = tm2->tm_sec;
    st.wMilliseconds = 0;
    return SetLocalTime(&st) ? STD_ERR_OK : STD_ERR_FAIL;
}

std_err_t unios_rtc_set(int year, int month, int day, int hour, int minute, int second) {
	struct tm dt = {0};
	dt.tm_year = year - 1900;
	dt.tm_mon = month - 1;
	dt.tm_mday = day;
	dt.tm_hour = hour;
	dt.tm_min = minute;
	dt.tm_sec = second;
	time_t rtcsec = mktime(&dt);
	return unios_rtcsec_set((unios_sec_t)rtcsec);
}

unios_datetime_t unios_rtc_local() {
    SYSTEMTIME st = { 0 };  
    GetLocalTime(&st);  // 获取当前时间 可精确到 ms
    unios_datetime_t tm = { 0 };
    tm.date.year = (uint16_t)st.wYear; tm.date.month = (uint8_t)st.wMonth; tm.date.day = (uint8_t)st.wDay; tm.date.weekday = (uint8_t)st.wDayOfWeek;
    tm.time.hour = (uint8_t)st.wHour; tm.time.minute = (uint8_t)st.wMinute; tm.time.second = (uint8_t)st.wSecond;
    //缺少属性
    //tm.tm_isdst = 0;
    return tm;
}

std_err_t unios_rtc_get(int *year, int *month, int *day, int *hour, int *minute, int *second, int *weekday) {
    unios_datetime_t dt = unios_rtc_local();
	//windows 年份不需要加上1900
    if(year) *year = dt.date.year;
    //windows 月份也不需要加1
	if(month) *month = dt.date.month;
	if(day) *day = dt.date.day;
	if(hour) *hour = dt.time.hour;
	if(minute) *minute = dt.time.minute;
	if(second) *second = dt.time.second;
	if(weekday) *weekday = dt.date.weekday;
	return STD_ERR_OK;
}

char* unios_rtc_local_string() {
	static char str[64];
    unios_datetime_t utm = unios_rtc_local();
	struct tm tm2;
    tm2.tm_year = utm.date.year; tm2.tm_mon = utm.date.month; tm2.tm_mday = utm.date.day; tm2.tm_wday = utm.date.weekday;
    tm2.tm_hour = utm.time.hour; tm2.tm_min = utm.time.minute; tm2.tm_sec  = utm.time.second;
	strftime(str, sizeof(str), "%c", &tm2);
	return str;
}

std_err_t unios_rtc_timezone_set(const char *tz) {
    // 注意: 在 Linux/POSIX 系统中，tzset() 会读取 TZ 环境变量来设置时区，但在 Windows 下，直接设置 TZ 可能不会生效 !!!
	return STD_ERR_NOT_SUPPORTED;
}

// ======================================================================================
// 关于文件
// ======================================================================================

unios_file_t unios_file_null() {
    return INVALID_HANDLE_VALUE;
}

bool unios_file_is_valid(unios_file_t uf) {
    return uf && ((HANDLE)uf) != INVALID_HANDLE_VALUE;
}

int64_t unios_file_size(const char *path) {
    struct stat fst;
    if(stat(path, &fst) < 0) { return -1; }
    return fst.st_size;
}

std_err_t unios_file_rename(const char *from, const char *to) {
    int err = rename(from, to);
    if(err) { return STD_ERR_FAIL; }
    return STD_ERR_OK;
}

std_err_t unios_file_delete(const char *path) {
	int err = remove(path);
    if(err) { return STD_ERR_FAIL; }
    return STD_ERR_OK;
}

unios_file_t unios_file_from_stdio_stream(FILE *fp) {
    return (unios_file_t)_get_osfhandle(_fileno(fp));
}

std_err_t unios_file_open(const char *path, unios_file_oflag_t oflag, unios_file_t *uf) {
    if(!path || !uf) return STD_ERR_INVALID_ARG;
    *uf = unios_file_null();

    if(oflag == UNIOS_FILE_OFLAG_READ) {
    	*uf = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    } else if(oflag == UNIOS_FILE_OFLAG_APPEND) {
    	*uf = CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    } else if(oflag == UNIOS_FILE_OFLAG_WRITE_TRUNCATE) {
    	*uf = CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    } else if(oflag == UNIOS_FILE_OFLAG_READ_WRITE) {
    	*uf = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    } else if(oflag == UNIOS_FILE_OFLAG_DEVICE_IO) {
    	*uf = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    } else {
    	return STD_ERR_INVALID_ARG;
    }

    if(unios_file_is_valid(*uf)) return STD_ERR_OK;
    return STD_ERR_FAIL;
}

void unios_file_destroy(unios_file_t *uf) {
    if(!uf || *uf == INVALID_HANDLE_VALUE) return;
    CloseHandle(*uf);
    *uf = INVALID_HANDLE_VALUE;
}

int64_t unios_file_datasize(unios_file_t uf) {
    if(!unios_file_is_valid(uf)) return -1;
    LARGE_INTEGER size;
    if(!GetFileSizeEx(uf, &size)) return -1;
    return size.QuadPart;
}

std_err_t unios_file_sync(unios_file_t uf) {
    if(!unios_file_is_valid(uf)) return STD_ERR_INVALID_ARG;
    return (FlushFileBuffers(uf) == TRUE) ? STD_ERR_OK : STD_ERR_FAIL;
}

std_err_t unios_file_read(unios_file_t uf, void *buf, size_t nbyte, int64_t offset, size_t *rbyte) {
    if(!unios_file_is_valid(uf) || !buf || !nbyte) return STD_ERR_INVALID_ARG;
    std_err_t err = STD_ERR_OK;
    if(rbyte) *rbyte = 0;
    
    do {
        // 设置文件当前偏移
        if(offset >= 0) {
            LARGE_INTEGER loffset = {.QuadPart = offset};
            if(!SetFilePointerEx(uf, loffset, NULL, FILE_BEGIN)) {
                loge("SetFilePointerEx() failed: %s\n", get_windows_error_msg());
                err = STD_ERR_FAIL; break;
            }
        }

        DWORD dlen = 0;
        BOOL ret = ReadFile(uf, buf, (DWORD)nbyte, &dlen, NULL);
        if(rbyte) *rbyte = dlen;
        if(!ret || dlen != nbyte) {
            loge("ReadFile() failed: %s", get_windows_error_msg());
            err = STD_ERR_FAIL; break;
        }
    } while(0);

    return err;
}

std_err_t unios_file_write(unios_file_t uf, const void *buf, size_t nbyte, int64_t offset, size_t *wbyte) {
    if(!unios_file_is_valid(uf) || !buf || !nbyte) return STD_ERR_INVALID_ARG;
    std_err_t err = STD_ERR_OK;
    if(wbyte) *wbyte = 0;
    
    do {
        // 设置文件当前偏移
        if(offset >= 0) {
            LARGE_INTEGER loffset = {.QuadPart = offset};
            if(!SetFilePointerEx(uf, loffset, NULL, FILE_BEGIN)) {
                loge("SetFilePointerEx() failed: %s\n", get_windows_error_msg());
                err = STD_ERR_FAIL; break;
            }
        }

        DWORD dlen = 0;
        BOOL ret = WriteFile(uf, buf, (DWORD)nbyte, &dlen, NULL);
        if(wbyte) *wbyte = dlen;
        if(!ret || dlen != nbyte) {
            loge("WriteFile() failed: %s", get_windows_error_msg());
            err = STD_ERR_FAIL; break;
        }
    } while(0);

    return err;
}

void* unios_file_mmap(unios_file_t uf, uint64_t offset, uint64_t size, uint64_t flags1, uint64_t flags2, void **mmptr/*out*/, uint64_t *mmsize/*out*/) {
    if(!unios_file_is_valid(uf) || !size) return NULL;

    // TODO 实现 windows 内存映射

    return NULL;
}

void unios_file_munmap(void *mmptr, uint64_t mmsize) {
    if(!mmptr || !mmsize) return;

    // TODO 实现 windows 内存映射解除
}

int unios_file_ioctl(unios_file_t uf, int cmd, void *opt, size_t opt_size) {
    if(!unios_file_is_valid(uf)) return -1;

    BOOL ret = DeviceIoControl(uf, cmd, opt, (DWORD)opt_size, NULL, 0, NULL, NULL);
    if(ret) return 0;

    return -1;
}


// ======================================================================================
// 关于 CPU
// ======================================================================================

// Helper function to count set bits in the processor mask.
static DWORD _count_set_bits(ULONG_PTR bitMask) {
    DWORD LSHIFT = sizeof(ULONG_PTR) * 8 - 1;
    ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT;
    DWORD bitSetCount = 0;

    for(DWORD i = 0; i <= LSHIFT; ++i) {
        bitSetCount += ((bitMask & bitTest) ? 1 : 0);
        bitTest /= 2;
    }

    return bitSetCount;
}

size_t unios_get_cpu_core_count() {
    // 并非所有系统上都存在 GetLogicalProcessorInformation() 函数，因此使用 GetProcAddress 函数获取其接口
    typedef BOOL(WINAPI * LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
    HMODULE hkernel32 = GetModuleHandleA("kernel32");
    if(!hkernel32) return 0;
    LPFN_GLPI glpi = (LPFN_GLPI)GetProcAddress(hkernel32, "GetLogicalProcessorInformation");
    if(!glpi) return 0; // 当前操作系统不支持

    // 分配数据结构内存
    DWORD slen = 0;
    glpi(NULL, &slen);
    if(GetLastError() != ERROR_INSUFFICIENT_BUFFER || !slen) return 0;
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pinfo = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(slen);
    if(!pinfo) return 0;

    // 获取 CPU 信息数据结构
    BOOL ret = glpi(pinfo, &slen);
    if(!ret || !slen) { free(pinfo); return 0; }

    // 统计 CPU 信息
    int numa = 0;       // 属于 NUMA 节点的逻辑处理器数量
    int pcpu = 0;       // 物理处理器数量
    int lcore = 0;      // 逻辑处理器(内核)数量
    int lthread = 0;    // 逻辑处理器(超线程/hyperthread)数量
    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION pinfo0 = pinfo;
    while(slen >= sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)) {
        if(pinfo->Relationship == RelationNumaNode) {
            // Non-NUMA systems report a single record of this type.
            numa++;
        } else if(pinfo->Relationship == RelationProcessorCore) {
            lcore++;
            // A hyperthreaded core supplies more than one logical processor.
            lthread += _count_set_bits(pinfo->ProcessorMask);
        } else if(pinfo->Relationship == RelationProcessorPackage) {
            pcpu++;
        }
        // 下一条信息
        pinfo++; slen -= sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
    }
    free(pinfo0);

    // 如果启用了超线程则返回超线程处理器数量，否则返回逻辑处理器数量
    return lthread ? lthread : lcore;
}


// ======================================================================================
// 关于线程
// ======================================================================================

std_err_t unios_thread_create(unios_thread_func_t func, void *cookie, unios_thread_priority_t priority, size_t stack_size, uint64_t cpu_affinity, unios_thread_t* pt) {
    if(!func || priority >= UNIOS_THREAD_PRIORITY_MAX) return STD_ERR_INVALID_ARG;

    size_t ncpu = 0;
    if(cpu_affinity) {
        // 获取 CPU 内核数量
        ncpu = unios_get_cpu_core_count();
        if(!ncpu) return STD_ERR_FAIL;
        if(cpu_affinity >= (uint64_t)(1ULL << ncpu)) return STD_ERR_OUT_OF_RANGE;
    }

    static const int PRIORITY_MAP[] = {
        THREAD_PRIORITY_NORMAL,
        THREAD_PRIORITY_IDLE,
        THREAD_PRIORITY_LOWEST,
        THREAD_PRIORITY_BELOW_NORMAL,
        THREAD_PRIORITY_NORMAL,
        THREAD_PRIORITY_ABOVE_NORMAL,
        THREAD_PRIORITY_HIGHEST,
        THREAD_PRIORITY_TIME_CRITICAL,
    };

    // 创建线程
    HANDLE thread = CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)func, cookie, 0, NULL);
    if(thread == NULL) return STD_ERR_FAIL;

    // 设置线程优先级
    if(priority > UNIOS_THREAD_PRIORITY_DEFAULT) {
        if(!SetThreadPriority(thread, PRIORITY_MAP[priority])) return STD_ERR_FAIL;
    }

    // 设置线程与 CPU 绑定关系
    if(cpu_affinity) {
        if(!SetThreadAffinityMask(thread, cpu_affinity)) return STD_ERR_FAIL;
    }

    if(pt) *pt = (unios_thread_t)thread;

    return STD_ERR_OK;
}

void unios_thread_close(unios_thread_t thread) {
    if(!thread) return;
    TerminateThread(thread, 0);
}

unios_thread_t unios_thread_this() {
    return (unios_thread_t)GetCurrentThread();
}


// ======================================================================================
// 关于临界区
// ======================================================================================

std_err_t unios_critical_section_init(unios_critical_section_t* pcs) {
    if(!pcs) return STD_ERR_INVALID_ARG;

    LPCRITICAL_SECTION pcs0 = (unios_critical_section_t)MALLOC0(CRITICAL_SECTION);
    if (!pcs0) return STD_ERR_NO_MEM;
    InitializeCriticalSection(pcs0);
    *pcs = (unios_critical_section_t)pcs0;

    return STD_ERR_OK;
}

std_err_t unios_critical_section_enter(unios_critical_section_t cs, long wait_timeout_ms) {
    if(!cs) return STD_ERR_INVALID_ARG;

    if(wait_timeout_ms < 0) return (EnterCriticalSection((LPCRITICAL_SECTION)cs), STD_ERR_OK);
    unios_ms_t tms = unios_ms_now();
    while(TryEnterCriticalSection((LPCRITICAL_SECTION)cs) == FALSE) {
        if(unios_ms_elapsed(tms) >= wait_timeout_ms) return STD_ERR_TIMEOUT;
        unios_us_delay(100);
    }

    return STD_ERR_OK;
}

void unios_critical_section_leave(unios_critical_section_t cs) {
    if(!cs) return;
    LeaveCriticalSection((LPCRITICAL_SECTION)cs);
}

void unios_critical_section_destroy(unios_critical_section_t *cs/*in,out*/) {
    if(!cs || !(*cs)) return;
    
    // 释放临界区
    unios_critical_section_leave((*cs));

    DeleteCriticalSection((LPCRITICAL_SECTION)(*cs));
    free((*cs));

    *cs = (unios_critical_section_t)NULL;
}


// ======================================================================================
// 关于锁
// ======================================================================================

std_err_t unios_lock_init(unios_lock_t* plock, size_t count) {
    if(!plock || count < 1) return STD_ERR_INVALID_ARG;

    HANDLE sem0 = CreateSemaphore(NULL, 1, (LONG)count, NULL);
    if (!sem0) return STD_ERR_NO_MEM;
    *plock = (unios_lock_t)sem0;

    return STD_ERR_OK;
}

std_err_t unios_lock(unios_lock_t lock, long wait_timeout_ms) {
    if(!lock) return STD_ERR_INVALID_ARG;

    DWORD tms = (wait_timeout_ms >= 0) ? (DWORD)wait_timeout_ms : INFINITE;
    DWORD ret = WaitForSingleObject((HANDLE)lock, tms);
    if (ret == WAIT_OBJECT_0) return STD_ERR_OK;
    if (ret == WAIT_TIMEOUT) return STD_ERR_TIMEOUT;
    return STD_ERR_FAIL;
}

std_err_t unios_unlock(unios_lock_t lock) {
    if(!lock) return STD_ERR_INVALID_ARG;

    return ReleaseSemaphore((HANDLE)lock, 1, NULL) ? STD_ERR_OK : STD_ERR_FAIL;
}

void unios_lock_destroy(unios_lock_t *lock) {
    if(!lock || !(*lock)) return;
    
    // 释放锁
    unios_unlock(*lock);

    CloseHandle((HANDLE)(*lock));
    *lock = NULL;
}


// ======================================================================================
// 关于事件
// ======================================================================================

std_err_t unios_event_init(unios_event_t* pevent, bool manual_reset, bool init_state) {
    if(!pevent) return STD_ERR_INVALID_ARG;

    HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!event) return STD_ERR_NO_MEM;
    *pevent = event;

    return STD_ERR_OK;
}

std_err_t unios_event_set(unios_event_t event, bool signal) {
    if(!event) return STD_ERR_INVALID_ARG;

    BOOL ret = signal ? SetEvent(event) : ResetEvent(event);
    return ret ? STD_ERR_OK : STD_ERR_FAIL;
}

std_err_t unios_event_wait(unios_event_t event, long wait_timeout_ms) {
    if(!event) return STD_ERR_INVALID_ARG;

    DWORD ms = (wait_timeout_ms > 0) ? (DWORD)wait_timeout_ms : INFINITE;
    DWORD ret = WaitForSingleObject(event, ms);
    if(ret == WAIT_OBJECT_0) return STD_ERR_OK;
    if(ret == WAIT_TIMEOUT) return STD_ERR_TIMEOUT;
    return STD_ERR_FAIL;
}

void unios_event_destroy(unios_event_t *pevent /*in,out*/) {
    if(!pevent || !(*pevent)) return;

    CloseHandle((HANDLE)(*pevent));
    *pevent = NULL;
}
